/**
 * 
 */
package wong.board.client.widgets;

import java.util.logging.Logger;

import wong.board.client.widgets.Button.ButtonCallback;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.UIObject;

/**
 * @author wong
 * 
 */
public class Window extends Container {
	public enum State {
		Opening, Opened, Minimizing, Minimized, Maximizing, Maximized, Closing, Closed;
	};

	private static Logger logger = Logger.getLogger(Window.class.getName());
	private Content header;
	private Label titleText;
	private ButtonsContainer<State> buttonsContainer;
	private Button<State> close;
	private Button<State> minimize;
	private Button<State> maximize;
	private Content content;
	private UIHandler handler;
	private Element rootElement;
	private State state;

	public Window() {
		this(null);
	}

	public Window(UIObject root) {
		setRoot(root);
		setStyleName(widgetsCss.window());
		//
		header = new ContentPanel();
		header.asWidget().addStyleName(widgetsCss.windowHeader());
		titleText = new Label();
		titleText.asWidget().addStyleName(widgetsCss.windowHeaderTitle());
		header.add(titleText);
		buttonsContainer = new ButtonsContainer<State>();
		buttonsContainer.addStyleName(widgetsCss.windowHeaderButtons());
		header.add(buttonsContainer);
		close = new Button<State>("");
		close.setTitle("Close");
		close.setValue(State.Closing);
		close.addStyleName(widgetsCss.windowHeaderButtonsClose());
		minimize = new Button<State>("");
		minimize.setTitle("Minimize");
		minimize.setValue(State.Minimizing);
		minimize.addStyleName(widgetsCss.windowHeaderButtonsMinimize());
		maximize = new Button<State>("");
		maximize.setTitle("Maximize");
		maximize.setValue(State.Maximizing);
		maximize.addStyleName(widgetsCss.windowHeaderButtonsMaximize());
		content = new ContentPanel();
		content.asWidget().addStyleName(widgetsCss.windowContent());
		buttonsContainer.addButton(maximize);
		buttonsContainer.addButton(minimize);
		buttonsContainer.addButton(close);
		handler = new UIHandler();
		buttonsContainer.addCallback(handler);
		//
		getContainer().add(header);
		getContainer().add(content);
		//
		this.setVisible(false);
		this.state = State.Closed;
	}

	public void setRoot(UIObject root) {
		if (root == null) {
			rootElement = RootPanel.get().getElement();
		} else {
			rootElement = root.getElement().getParentElement();
		}
	}

	public HasText getTitleText() {
		return titleText;
	}

	public Content getContent() {
		return content;
	}

	//

	public void open() {
		//
		if (!isAttached()) {
			rootElement.appendChild(this.getElement());
			setVisible(true);
			this.onAttach();
			this.registerAnimationEvents();
			this.addStyleName(widgetsCss.windowOpening());
			this.state = State.Opening;
			adjustPosition();
		}
	}

	protected void onOpened() {
		this.removeStyleName(widgetsCss.windowOpening());
		this.removeStyleName(widgetsCss.windowMinimizing());
		this.removeStyleName(widgetsCss.windowMinimized());
		this.removeStyleName(widgetsCss.windowMaximizing());
		this.removeStyleName(widgetsCss.windowMaximized());
		this.state = State.Opened;
	}

	protected void adjustPosition() {
		// TODO adjust size and position according to parent.
		int thisW = this.getOffsetWidth();
		int thisH = this.getOffsetHeight();
		logger.info("this: " + thisW + " x " + thisH);
		int rootW = com.google.gwt.user.client.Window.getClientWidth();
		int rootH = com.google.gwt.user.client.Window.getClientHeight();
		logger.info("root: " + rootW + " x " + rootH);
		int x = ((rootW - thisW) / 2);
		int y = ((rootH - thisH) / 2);
		//
		Style style = this.getElement().getStyle();
		style.setPosition(Position.FIXED);
		style.setLeft(x, Unit.PX);
		style.setTop(y, Unit.PX);
		// style.setWidth(value, unit);
	}

	public void close() {
		if (isAttached()) {
			this.addStyleName(widgetsCss.windowClosing());
			this.state = State.Closing;
		}
	}

	protected void onClosed() {
		if (isAttached()) {
			onDetach();
			setVisible(false);
			rootElement.removeChild(this.getElement());
			this.unregisterAnimationEvents();
			this.removeStyleName(widgetsCss.windowClosing());
			this.removeStyleName(widgetsCss.windowOpening());
			this.removeStyleName(widgetsCss.windowMinimizing());
			this.removeStyleName(widgetsCss.windowMinimized());
			this.removeStyleName(widgetsCss.windowMaximizing());
			this.removeStyleName(widgetsCss.windowMaximized());
			this.state = State.Closed;
		}
	}

	public void minimize() {
		if (isAttached() && (state == State.Opened || state == State.Maximized)) {
			this.addStyleName(widgetsCss.windowMinimizing());
			this.removeStyleName(widgetsCss.windowOpening());
			this.removeStyleName(widgetsCss.windowMinimized());
			this.removeStyleName(widgetsCss.windowMaximized());
			this.state = State.Minimizing;
		}
	}

	protected void onMinimized() {
		if (isAttached()) {
			this.removeStyleName(widgetsCss.windowMinimizing());
			this.state = State.Minimized;
			this.addStyleName(widgetsCss.windowMinimized());
			this.removeStyleName(widgetsCss.windowOpening());
			this.removeStyleName(widgetsCss.windowMaximizing());
			this.removeStyleName(widgetsCss.windowMaximized());
		}
	}

	public void maximize() {
		if (isAttached() && (state == State.Opened || state == State.Minimized)) {
			this.addStyleName(widgetsCss.windowMaximizing());
			this.removeStyleName(widgetsCss.windowOpening());
			this.removeStyleName(widgetsCss.windowMinimizing());
			this.removeStyleName(widgetsCss.windowMinimized());
			this.removeStyleName(widgetsCss.windowMaximized());
			this.state = State.Maximizing;
		}
	}

	protected void onMaximized() {
		if (isAttached()) {
			this.removeStyleName(widgetsCss.windowMaximizing());
			this.removeStyleName(widgetsCss.windowOpening());
			this.removeStyleName(widgetsCss.windowMinimized());
			this.removeStyleName(widgetsCss.windowMaximizing());
			this.removeStyleName(widgetsCss.windowMaximized());
			this.state = State.Maximized;
			this.addStyleName(widgetsCss.windowMaximized());
		}
	}

	//

	protected void onEvent(NativeEvent nativeEvent, String type, String name, float elapsedTime) {
		if (type != null && type.equals("animationend")) {
			switch (state) {

			case Opened:
			case Minimized:
			case Maximized:
			case Closed:
				return;

			case Opening:
				onOpened();
				return;

			case Minimizing:
				onMinimized();
				return;

			case Maximizing:
				onMaximized();
				return;

			case Closing:
				onClosed();
				return;

			default:
				break;
			}
			logger.warning("unhandled event @ state '" + state + "': " + nativeEvent + " / type: " + type
					+ " / name: " + name + " / elapsedTime: " + elapsedTime + "");
		}
	}

	private final native void registerAnimationEvents() /*-{
		var wnd = this;

		var listener = function(event) {
			wnd.@wong.board.client.widgets.Window::onEvent(Lcom/google/gwt/dom/client/NativeEvent;Ljava/lang/String;Ljava/lang/String;F)(event, event.type, event.animationName, event.elapsedTime);
		};

		var e = wnd.@wong.board.client.widgets.Window::getElement()();

		e.addEventListener("animationstart", listener, false);
		e.addEventListener("animationend", listener, false);
		e.addEventListener("animationiteration", listener, false);

	}-*/;

	private final native void unregisterAnimationEvents() /*-{

															}-*/;

	private class UIHandler implements ButtonCallback<State> {

		@Override
		public void onAction(Button<State> button, DomEvent<?> event) {
			State desiredState = button.getValue();
			switch (desiredState) {
			case Closing:
				close();
				break;

			case Minimizing:
				minimize();
				break;

			case Maximizing:
				maximize();
				break;

			default:
				break;
			}
		}
	}

}
